package org.yelongframework.model.support.mybatis.mapper;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.stream.Collectors;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.type.JdbcType;
import org.yelongframework.commons.lang.StringUtil;
import org.yelongframework.core.sql.param.SqlParam;
import org.yelongframework.core.sql.param.SqlParamEntry;
import org.yelongframework.model.support.mybatis.mapping.parameter.MappingParameterTypeDeducer;
import org.yelongframework.model.support.mybatis.sql.MybatisParamMap;
import org.yelongframework.mybatis.mapper.GenericMapper;
import org.yelongframework.mybatis.mapper.GenericMapperParam;
import org.yelongframework.sql.SqlRuntimeException;

/**
 * 默认实现
 * 
 * @since 1.0.0
 */
public class DefaultGenericMapperParamBuilder implements GenericMapperParamBuilder {

	/** 映射此参数类型推断器 */
	private MappingParameterTypeDeducer mappingParameterTypeDeducer;

	// ==================================================构造器==================================================

	/**
	 * @param mappingParameterTypeDeducer 映射此参数类型推断器
	 */
	public DefaultGenericMapperParamBuilder(MappingParameterTypeDeducer mappingParameterTypeDeducer) {
		this.mappingParameterTypeDeducer = Objects.requireNonNull(mappingParameterTypeDeducer,
				"mappingParameterTypeDeducer");
	}

	// ==================================================根据数组构建参数==================================================

	@Override
	public GenericMapperParam build(final String sql, final Object[] params) {
		Map<String, Object> mybatisParams = new HashMap<String, Object>();
		StringBuilder sb = new StringBuilder(sql);
		if (ArrayUtils.isNotEmpty(params)) {
			for (int i = 0; i < params.length; i++) {
				int index = sb.indexOf("?");
//				String paramName = generateParamName();
				String paramName = "PARAM" + i;
				mybatisParams.put(paramName, params[i]);
				JdbcType jdbcType = mappingParameterTypeDeducer.deduce(params[i]);
				// 设置字段类型
				if (null != jdbcType) {
					paramName += ",jdbcType = " + jdbcType.name();
				}
				sb.replace(index, index + 1, " #{" + paramName + "} ");
			}
		}
		return new GenericMapperParam(sb.toString(), mybatisParams);
	}

	// ==================================================根据Mybatis参数构建参数==================================================

	@Override
	public GenericMapperParam build(String sql, MybatisParamMap mybatisParamMap) {
		Map<String, Object> params = new HashMap<String, Object>(2);
		if (null != mybatisParamMap) {
			params.put(mybatisParamMap.getMybatisParamMapPropertyName(), mybatisParamMap);
		}
		return new GenericMapperParam(sql, params);
	}

	// ==================================================根据Sql参数构建参数==================================================

	@Override
	public GenericMapperParam build(final String sql, final SqlParam sqlParam) {
		// 参数
		GenericMapperParam genericMapperParam = new GenericMapperParam();
		// SQL
		StringBuilder sqlBuilder = new StringBuilder(sql);
		// 参数索引映射
		Map<Integer, SqlParamEntry> parameterIndexMap = sqlParam.getParameterIndexMap();
		// 根据索引进行排序
		List<Entry<Integer, SqlParamEntry>> parameterIndexList = parameterIndexMap.entrySet().stream()
				.sorted((x, y) -> {
					return Integer.compare(y.getKey(), x.getKey());
				}).collect(Collectors.toList());
		// 解析SQL与参数
		for (Entry<Integer, SqlParamEntry> entry : parameterIndexList) {
			Integer paramIndex = entry.getKey();
			// 占位符索引
			int index = StringUtil.indexOf(sqlBuilder.toString(), "?", paramIndex);
			if (index == -1) {
				throw new SqlRuntimeException("不匹配的参数位置");
			}
			SqlParamEntry sqlParamEntry = entry.getValue();
			// 参数名称
			final String paramName = generateParamName(paramIndex, sqlParamEntry);
			// mybatis参数占位符
			final String mybatisParamPlaceholder = buildMybatisParamPlaceholder(paramIndex, paramName, sqlParamEntry);
			// 参数值
			final Object paramValue = sqlParamEntry.getValue();
			// value
			genericMapperParam.put(paramIndex, paramName, paramValue);
			// 修改?
			sqlBuilder.replace(index, index + 1, " #{" + mybatisParamPlaceholder + "} ");
		}
		genericMapperParam.setSql(sqlBuilder.toString());
		return genericMapperParam;
	}

	// ==================================================参数名称==================================================

	/**
	 * 生成参数名称
	 * 
	 * @param paramIndex    参数索引
	 * @param sqlParamEntry SQL参数实体
	 * @return 参数名称
	 */
	protected String generateParamName(final Integer paramIndex, final SqlParamEntry sqlParamEntry) {
		// return UUID.randomUUID().toString().replace("-", "").toLowerCase();
		return "PARAM" + paramIndex;
	}

	// ==================================================参数占位符==================================================

	/**
	 * 构建mybatis参数占位符
	 * 
	 * @param paramIndex    参数索引
	 * @param paramName     参数名称
	 * @param sqlParamEntry SQL参数实体
	 * @return mybatis参数占位符
	 */
	protected String buildMybatisParamPlaceholder(final Integer paramIndex, final String paramName,
			final SqlParamEntry sqlParamEntry) {
		StringBuilder mybatisParamPlaceholder = new StringBuilder(paramName);
		// jdbcType
		JdbcType jdbcType = null;
		Integer sqlType = sqlParamEntry.getSqlType();
		if (null != sqlType) {
			jdbcType = JdbcType.forCode(sqlType);
		} else {
			jdbcType = mappingParameterTypeDeducer.deduce(sqlParamEntry.getValue());
		}
		if (null != jdbcType) {
			mybatisParamPlaceholder.append(", jdbcType = " + jdbcType);
		}
		// mode
		mybatisParamPlaceholder.append(", mode = " + sqlParamEntry.getMode().name());
		// resultMap
		String resultMap = sqlParamEntry.getExtendAttribute("resultMap");
		if (StringUtils.isBlank(resultMap)) {
			if (null != sqlType && sqlType == JdbcType.CURSOR.TYPE_CODE) {
				// 默认为map
				resultMap = GenericMapper.RESULT_MAP_ID;
			}
		}
		if (StringUtils.isNotBlank(resultMap)) {
			mybatisParamPlaceholder.append(", resultMap = " + resultMap);
		}
		return mybatisParamPlaceholder.toString();
	}

}
